package com.hopedove.tempdemo.utils

import android.graphics.*

sealed class SideIndicate(val order: Boolean = true) {
    class Left(order: Boolean = true) : SideIndicate(order)
    class Right(order: Boolean = true) : SideIndicate(order)
    class Top(order: Boolean = true) : SideIndicate(order)
    class Bottom(order: Boolean = true) : SideIndicate(order)
}


data class GridCellBitmap(val content: Bitmap, val x: Int, val y: Int)
enum class GridCellCoordinate {
    LeftTop, LeftBottom, RightTop, RightBottom
}

class GridBitmapBuilder(
    private val rowNum: Int,
    private val colNum: Int,
    private val size: Int = 50,
    private val borderSize: Int = 1,
    private val sideIndicate: Set<SideIndicate>
) {

    private var bitmap: Bitmap? = null
    private val canvas = Canvas()

    // 如有 Top,Bottom 则行数要相应增加
    // 如有 Left,Right 则列数要相应增加
    private val rows =
        rowNum + sideIndicate.count { it is SideIndicate.Top || it is SideIndicate.Bottom }

    private val cols =
        colNum + sideIndicate.count { it is SideIndicate.Left || it is SideIndicate.Right }


    fun build(zeroBase: Boolean = true): Bitmap? {
        bitmap = buildGrid()
        drawSideIndicate(zeroBase)
        return bitmap
    }

    fun drawCellBitmaps(bitmaps: List<GridCellBitmap>, coordinate: GridCellCoordinate) {

        val bmps = bitmaps.map {
            val (x, y) = transformCoordinate(it.x, it.y, coordinate)
            val cellX = sideIndicate.find { it is SideIndicate.Left }?.let { x + 1 } ?: x
            val cellY = sideIndicate.find { it is SideIndicate.Top }?.let { y + 1 } ?: y
            return@map GridCellBitmap(it.content, cellX, cellY)
        }

        bmps.forEach { cell ->
            val baseXOffset = borderSize * (cell.x + 1) + size * cell.x
            val baseYOffset = borderSize * (cell.y + 1) + size * cell.y
            val r = Rect(0, 0, size, size)
            r.offset(baseXOffset, baseYOffset)

            canvas.drawBitmap(
                cell.content,
                null,
                r,
                null
            )
        }
    }

    private fun buildGrid(): Bitmap? {

        val width = (cols * size) + (cols + 1) * borderSize
        val height = (rows * size) + (rows + 1) * borderSize

        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        canvas.setBitmap(bitmap)
        val paint = Paint().apply {
            color = Color.BLACK
        }

        // rows
        for (i in 0..rows) {
            val offset = i * (borderSize + size)
            val startX = 0f
            val startY = if (offset == 0) 0.5f else offset.toFloat()
            val endX = width.toFloat()
            val endY = if (offset == 0) 0.5f else offset.toFloat()
            canvas.drawLine(startX, startY, endX, endY, paint)
        }

        // columns
        for (i in 0..cols) {
            val offset = i * (borderSize + size)
            val startX = if (offset == 0) 0.5f else offset.toFloat()
            val startY = 0f
            val endX = if (offset == 0) 0.5f else offset.toFloat()
            val endY = height.toFloat()
            canvas.drawLine(startX, startY, endX, endY, paint)
        }

        return bitmap
    }

    private fun drawSideIndicate(zeroBase: Boolean) {
        sideIndicate.find { it is SideIndicate.Left }
            ?.let { drawLeftSideIndicate(it.order, zeroBase) }
        sideIndicate.find { it is SideIndicate.Top }
            ?.let { drawTopSideIndicate(it.order, zeroBase) }
        sideIndicate.find { it is SideIndicate.Bottom }
            ?.let { drawBottomIndicate(it.order, zeroBase) }
        sideIndicate.find { it is SideIndicate.Right }
            ?.let { drawRightIndicate(it.order, zeroBase) }
    }

    private fun drawLeftSideIndicate(order: Boolean, zeroBase: Boolean = true) {
        bitmap?.let { bmp ->
            val paint = Paint().apply {
                color = Color.BLACK
            }
            for (num in 0 until rows) {
                val index = genIndex(num, rows, order, zeroBase).toString()
                val rect = Rect()
                paint.getTextBounds(index, 0, index.length, rect)
                if (rect.width() <= size && rect.height() < size) {
                    val baseXOffset = borderSize
                    val baseYOffset = borderSize * (num + 1) + size * num
                    val xOffset = (size - rect.width()) / 2
                    val yOffset = size - (size - rect.height()) / 2

                    val x = baseXOffset + xOffset
                    val y = baseYOffset + yOffset
                    canvas.drawText(index, x.toFloat(), y.toFloat(), paint)
                }
            }
        }
    }

    private fun genIndex(index: Int, total: Int, order: Boolean, zeroBase: Boolean): Int {
        val num = if (order) index else total - index - 1
        return if (zeroBase) num else num + 1
    }

    private fun drawTopSideIndicate(order: Boolean, zeroBase: Boolean) {
        bitmap?.let { bmp ->
            val paint = Paint().apply {
                color = Color.BLACK
            }
            for (num in 0 until cols) {
                val index = genIndex(num, rows, order, zeroBase).toString()

                val rect = Rect()
                paint.getTextBounds(index, 0, index.length, rect)
                if (rect.width() <= size && rect.height() < size) {
                    val baseXOffset = borderSize * (num + 1) + size * num
                    val baseYOffset = borderSize
                    val xOffset = (size - rect.width()) / 2
                    val yOffset = size - (size - rect.height()) / 2

                    val x = baseXOffset + xOffset
                    val y = baseYOffset + yOffset
                    canvas.drawText(index, x.toFloat(), y.toFloat(), paint)
                }
            }
        }
    }

    private fun drawRightIndicate(order: Boolean, zeroBase: Boolean) {
        bitmap?.let { bmp ->
            val paint = Paint().apply {
                color = Color.BLACK
            }
            for (num in 0 until rows) {
                val index = genIndex(num, rows, order, zeroBase).toString()
                val rect = Rect()
                paint.getTextBounds(index, 0, index.length, rect)
                if (rect.width() <= size && rect.height() < size) {
                    val baseXOffset = borderSize * cols + size * (cols - 1)
                    val baseYOffset = borderSize * (num + 1) + size * num
                    val xOffset = (size - rect.width()) / 2
                    val yOffset = size - (size - rect.height()) / 2

                    val x = baseXOffset + xOffset
                    val y = baseYOffset + yOffset
                    canvas.drawText(index, x.toFloat(), y.toFloat(), paint)
                }
            }
        }
    }

    private fun drawBottomIndicate(order: Boolean, zeroBase: Boolean) {
        bitmap?.let { bmp ->
            val paint = Paint().apply {
                color = Color.BLACK
            }
            for (num in 0 until cols) {
                val index = genIndex(num, rows, order, zeroBase).toString()

                val rect = Rect()
                paint.getTextBounds(index, 0, index.length, rect)
                if (rect.width() <= size && rect.height() < size) {
                    val baseXOffset = borderSize * (num + 1) + size * num
                    val baseYOffset = borderSize * rows + size * (rows - 1)
                    val xOffset = (size - rect.width()) / 2
                    val yOffset = size - (size - rect.height()) / 2

                    val x = baseXOffset + xOffset
                    val y = baseYOffset + yOffset
                    canvas.drawText(index, x.toFloat(), y.toFloat(), paint)
                }
            }
        }
    }


    private fun transformCoordinate(
        x: Int,
        y: Int,
        coordinate: GridCellCoordinate
    ): Pair<Int, Int> {
        return when (coordinate) {
            GridCellCoordinate.LeftTop -> Pair(x, y)
            GridCellCoordinate.RightTop -> Pair(colNum - 1 - x, y)
            GridCellCoordinate.LeftBottom -> Pair(x, rowNum - 1 - y)
            GridCellCoordinate.RightBottom -> Pair(colNum - 1 - x, rowNum - 1 - y)
        }
    }

}

